﻿#if UNITY_STANDALONE || UNITY_ANDROID || UNITY_IOS || UNITY_WSA || UNITY_WEBGL
using System;
using System.Collections.Generic;
using Net.Client;
using Net.Common;
using Net.Component;
using Net.Helper;
using Net.Share;
using Net.System;
using UnityEngine;

namespace Net.UnityComponent
{
    /// <summary>
    /// 网络物体标识组件
    /// </summary>
    [DisallowMultipleComponent]
    [DefaultExecutionOrder(1000)]
    public class NetworkObject : MonoBehaviour
    {
        internal static long IDENTITY { get; private set; } = -1;
        internal static long IdentityStart { get; private set; }
        internal static long IdentityEnd { get; private set; }
        internal static Queue<long> IDENTITY_POOL = new();
        public static int Capacity { get; private set; }
        public static bool IsInitIdentity => IDENTITY != -1;
        [Tooltip("自定义唯一标识, 当值不为0后, 可以不通过NetworkSceneManager的collection去设置, 直接放在设计的场景里面, 不需要做成预制体")]
        [SerializeField] private long identity; //可以设置的id --设置自定义id请调用SetIdentity()
        [Obsolete("使用prefabHash代替")]
        public int registerObjectIndex { get => prefabHash; set => prefabHash = value; }
        [Tooltip("网络预制体哈希值, 用于在其他客户端实例化对应的网络物体所用")]
        [DisplayOnly]
        public int prefabHash;
        internal List<NetworkBehaviour> networkBehaviours = new();
        public MyDictionary<ushort, SyncVarInfo> syncVarInfos = new();
        public bool IsDispose { get; internal set; }
        /// <summary>
        /// 此物体是否是本机实例化？
        /// </summary>
        public bool IsLocal { get; set; }
        /// <summary>
        /// 每个网络对象的唯一标识
        /// </summary>
        public long Identity { get; set; }
        /// <summary>
        /// 获取或设置是否初始化
        /// </summary>
        public bool IsInitialize { get; set; }
        /// <summary>
        /// 是否可以发送销毁指令给服务器
        /// </summary>
        public bool CanDestroy { get; set; } = true;
        public List<NetworkBehaviour> NetworkBehaviours => networkBehaviours;

        public virtual void Start()
        {
            Init();
        }

        public void ReInit()
        {
            IsInitialize = false;
            Init();
        }

        public void Init()
        {
            if (IsInitialize)
                return;
            IsInitialize = true;
            if (IDENTITY == -1 && identity == 0) //全局netobj
            {
                Debug.LogError("网络标识未初始化，请调用NetworkObject.Init(5000);初始化");
                Destroy(gameObject);
                return;
            }
            var sm = NetworkSceneManager.I;
            if (sm == null)
            {
                Debug.LogError("没有找到NetworkSceneManager组件！NetworkIdentity组件无效！");
                Destroy(gameObject);
                return;
            }
            if (Identity != 0U) //用代码自定义唯一标识处理
            {
                goto J1;
            }
            if (identity > 0) //这个是小写, 可自定义的字段
            {
                Identity = identity;
                goto J1;
            }
            if (IDENTITY_POOL.Count > 0)
            {
                Identity = IDENTITY_POOL.Dequeue();
                goto J1;
            }
            if (IDENTITY < IdentityEnd)
            {
                Identity = IDENTITY++;
                goto J1;
            }
            else
            {
                Debug.LogError("网络标识已用完! 如果有需要请加大网络标识数量NetworkObject.Init(10000);");
                Destroy(gameObject);
                return;
            }
        J1:
            IsLocal = true;
            if (!sm.identitys.TryAdd(Identity, this, out var oldNetObj))
            {
                if (oldNetObj == this | oldNetObj == null)
                    return;
                oldNetObj.Identity = -1;
                Debug.Log($"uid:{Identity}发生了两次实例化! 本地的实例化和网络同步下来的identity冲突");
                Destroy(oldNetObj.gameObject);
            }
        }

        public void InitAll(in Operation opt = default)
        {
            Init();
            var nbs = GetComponentsInChildren<NetworkBehaviour>(true);
            foreach (var nb in nbs)
            {
                nb.Init(opt);
            }
        }
        /// <summary>
        /// 添加字段和属性同步
        /// </summary>
        /// <param name="networkBehaviour"></param>
        /// <param name="target"></param>
        public void AddSyncVar(NetworkBehaviour networkBehaviour, object target)
        {
            int syncVarID = networkBehaviour.NetComponentID * 50; //每个组件和定义[SyncVar]同步字段和属性最多50个
            ClientBase.Instance.RpcAdapter.AddRpc(target, AddRpcMode.None, (info) =>
            {
                info.id = (ushort)(syncVarID + networkBehaviour.SyncVarID++);
                syncVarInfos.Add(info.id, info);
            });
        }

        public void CheckSyncVar()
        {
            if (syncVarInfos.Count == 0)
                return;
            var buffer = SyncVarHelper.CheckSyncVar(IsLocal, syncVarInfos);
            if (buffer != null)
                SyncVarSend(buffer);
        }

        private void SyncVarSend(byte[] buffer)
        {
            NetworkSceneManager.Instance.AddOperation(new Operation(NetCmd.SyncVarNetObjHash, Identity)
            {
                uid = ClientBase.Instance.UserId,
                index = prefabHash,
                buffer = buffer
            });
        }

        internal void SyncVarHandler(in Operation opt)
        {
            if (opt.uid == ClientBase.Instance.UserId)
                return;
            SyncVarHelper.SyncVarHandler(syncVarInfos, opt.buffer);
        }

        internal void RemoveSyncVar(NetworkBehaviour target)
        {
            SyncVarHelper.RemoveSyncVar(syncVarInfos, target);
        }

        internal void PropertyAutoCheckHandler()
        {
            for (int i = 0; i < networkBehaviours.Count; i++)
            {
                var networkBehaviour = networkBehaviours[i];
                if (networkBehaviour == null)
                    continue;
                if (!networkBehaviour.CheckEnabled())
                    continue;
                networkBehaviour.OnPropertyAutoCheck();
            }
        }

        public virtual void OnDestroy()
        {
            if (IsDispose)
                return;
            IsDispose = true;
            if (Identity == -1)
                return;
            var sm = NetworkSceneManager.Instance;
            if (sm == null)
                return;
            if (!IsLocal | Identity < 10000) //0-10000是场景可用标识
                return;
            if (!CanDestroy)
                return;
            if (ClientBase.Instance == null)
                return;
            if (!ClientBase.Instance.Connected)
                return;
            SendDestroyCommand();
        }

        public void SendDestroyCommand()
        {
            var sm = NetworkSceneManager.Instance;
            if (sm == null)
                return;
            sm.AddOperation(new Operation(OperCommand.Destroy, Identity));
        }

        internal static void PushIdentity(long identity)
        {
            if (IDENTITY == -1)
                return;
            if (identity >= IdentityStart && identity < IdentityEnd)
                IDENTITY_POOL.Enqueue(identity);
        }

        /// <summary>
        /// 初始化网络唯一标识
        /// </summary>
        /// <param name="capacity">一个客户端可以用的唯一标识容量</param>
        public static void Init(int capacity = 20000)
        {
            //要实时可初始化，要不然每次切换场景都无法初始化id，或者切换账号后uid变了，就得不到真正的identity值了
            Capacity = capacity;
            (long start, long end) = GetUserIdOffset(ClientBase.Instance.UserId);
            IDENTITY = start;
            IdentityStart = start;
            IdentityEnd = end;
            IDENTITY_POOL.Clear();
        }

        /// <summary>
        /// 释放初始化的identity
        /// </summary>
        public static void UnInit()
        {
            IDENTITY = -1;
            IdentityStart = 0;
            IdentityEnd = 0;
            Capacity = 0;
            IDENTITY_POOL.Clear();
        }

        /// <summary>
        /// 获取玩家id的偏移量, 此方法算出来每个玩家可实例化多少个网络对象
        /// </summary>
        /// <param name="userId"></param>
        /// <returns>返回此uid的网络物体Identity的开始位置和结束位置</returns>
        public static (long start, long end) GetUserIdOffset(long userId)
        {
            //0 - 4294967295(uint.MaxValue)是公共id，4294967295 - 281474976710655(6字节48位)是玩家uid
            //默认每个玩家可以实例化20000个网络物体，并且id都是唯一的，如果超出则导致id冲突
            var baseValue = 1L << 48; // 2^48
            var uniqueValue = userId;
            var rangeSize = Capacity + 1;
            var start = baseValue + ((uniqueValue - 1) * rangeSize);
            var end = start + rangeSize - 1;
            return (start, end);
        }

        public void NetworkUpdate(bool isNetworkTick)
        {
            for (int i = 0; i < networkBehaviours.Count; i++)
            {
                var networkBehaviour = networkBehaviours[i];
                if (networkBehaviour == null)
                    continue;
                if (!networkBehaviour.CheckEnabled())
                    continue;
                networkBehaviour.NetworkUpdate(isNetworkTick);
                if (!IsLocal | isNetworkTick)
                    continue;
                networkBehaviour.OnPropertyAutoCheck();
            }
            if (isNetworkTick)
                CheckSyncVar();
        }

#if UNITY_EDITOR
        private void OnValidate()
        {
            int newPrefabHash = 0;
            var assetPath = UnityEditor.AssetDatabase.GetAssetPath(gameObject);
            if (!string.IsNullOrEmpty(assetPath) && assetPath.EndsWith(".prefab"))
                newPrefabHash = assetPath.CRC32();
            if (prefabHash != newPrefabHash && newPrefabHash != 0)
            {
                prefabHash = newPrefabHash;
                UnityEditor.EditorUtility.SetDirty(gameObject);
            }
        }
#endif 
    }
}
#endif